/* 

==========================================================

DX490a - Summer 2010

Instructor: Stelios Manousakis

==========================================================

Class 6.1:

Interfacing 2: MIDI in SuperCollider

Contents: 

• Connecting

• Receiving Data

- MIDIIn

- Responders

• Sending data

• MIDI clock

- Sending MIDI Clock data

- Receiving MIDI Clock data

• MIDI files

• Utilities

• Third Party Implementations

- dewdrop library

- wslib

- JITMIDIKtl

- rd_ctl

==========================================================

*/




// ================= MIDI IN SUPECOLLIDER =================


// ====== CONNECTING ======

// MIDI support in SuperCollider quite extensive, and any device that your system recognizes SC can talk to. The MIDI-related classes are little more than hardware abstractions.

// In SC, MIDI is implemented as a service. As such, there is a single MIDI input function that handles all messages - although you can choose to only use a specific subset of those messages within SC. 

// In order to use MIDI, you have to instantiate the service: you do that by initializing the MIDIClient, which is the class that communicates with the CoreMIDI of your machine. This looks at the connected devices (MIDIEndPoint), and assigns a unique ID to each device, that you can later access by connecting a MIDIIn port. 


// here is how to connect a device:

// • Step 1: Initialize the client

MIDIClient.init; 

// • Step 2: Start the responder. Ex:

MIDIIn.connect

// • Step 3: Define what midi method you want to use, and create a function to do something with the data. Ex:

MIDIIn.control = { arg src, chan, num, val; 

if (num == 23, {p.play});

if (num ==33, {p.release});

[chan,num,val].postln; };



// For more general information, look here: UsingMIDI



// ====== RECEIVING DATA ======


// ------ MIDIIn --

// MIDIIn receives incoming data from a port, and has a number of methods for receiving different kinds of data. These are:

noteOff, noteOn, polytouch, control, program, touch, bend, sysex, sysrt, smpte

// Evaluate the following chunk to post any incoming data from any port:


(

MIDIIn.connect(0, MIDIClient.sources.at(0)); // connect to port 0, you can change this to the port your device is connected

// register functions:

MIDIIn.noteOff = { arg src, chan, num, vel; [chan,num,vel / 127].postln; };

MIDIIn.noteOn = { arg src, chan, num, vel; [chan,num,vel / 127].postln; };

MIDIIn.polytouch = { arg src, chan, num, vel; [chan,num,vel / 127].postln; };

MIDIIn.control = { arg src, chan, num, val; [chan,num,val].postln; };

MIDIIn.program = { arg src, chan, prog; [chan,prog].postln; };

MIDIIn.touch = { arg src, chan, pressure; [chan,pressure].postln; };

MIDIIn.bend = { arg src, chan, bend; [chan,bend - 8192].postln; };

MIDIIn.sysex = { arg src, sysex; sysex.postln; };

MIDIIn.sysrt = { arg src, chan, val; [chan,val].postln; };

MIDIIn.smpte = { arg src, chan, val; [chan,val].postln; };

)


// Have a look at the MIDIIn file for more information

// NOTE:  You can only have one MIDIIn instance active at a time!


// ------ Responders --

// Many interfaces only send a specific kind of data, so you don't have to be receiving everything; instead, you can use one of the MIDIResponder subclasses. These also allow you to have any number of responders active simultaneously. There are 6 different responders, and all function similarly, inheriting many characteristics from their common superclass:

NoteOnResponder

NoteOffResponder

CCResponder

BendResponder

TouchResponder

ProgramChangeResponder


// ====== SENDING DATA ======

// MIDIOut is the class that allows you to control MIDI devices from within SuperCollider.  Unlike with MIDIIn, you can create multiple MIDIOut instances, but you have to provide the port number (index of the MIDIEndPoint in the MIDIClient.destinations array) and - optionally - the unique ID (uid). You can also define the device to use by name with the .newByName method, which can be handy if your ports change - which is likely if you're using several devices. 

// You can get the available ports like this

MIDIClient.destinations

// You can send any type of data that you can receive: noteOff, noteOn, polytouch, control, program, touch, bend, sysex, sysrt, smpte


// Besides sending to external devices, you can also send MIDI data to other software. Naturally, OSC messages are prefferable, but not all software is OSC-enabled, so you may have to use MIDI. You can find information on how to do that in different Operating Systems in the MIDIOut helpfile



// ====== MIDI CLOCK ======

// MIDI provides you with the ability to synchronize devices by sending ticks - although as this is the MIDI world, don't expect anything spectacularly robust or with great resolution. More information can be found in the UsingMIDI file.


// ------ Sending MIDI clock data --

// You can send clock data with MIDIOut. There is also MIDIClockOut (part of crucial_lib, which is included in the SC distribution), a more convenient specialized class sending clock data out that is controllable by a TempoClock



// ------ Receiving MIDI clock data --

// You can synchronize SC to an external MIDI clock with sysrt or with smpte. sysrt uses 24 ticks per quarter note. The MIDIIn class has has methods for both (MIDIIn.sysrt and MIDIn.smpte. There is also MIDISyncClock from the dewdrop library (a quark), which implements this functionaity



// ====== MIDI FILES ======

// You can use the [SimpleMIDIFile] Class from the wslib quark to read or write MIDI files in and from SC. Here is an example from Wouter Snoei (the wslib creator):


// download a fugue

"curl http://www.bachcentral.com/BachCentral/AOF/dou1.mid -o ~/Desktop/dou1.mid".unixCmd;

// read it

m = SimpleMIDIFile.read( "~/Desktop/dou1.mid" );

// plot it:

m.plot;

// play it (an undocumented 'experimental' method), only plays one voice

m.p.play;



// ====== UTILITIES ======

// A couple of handy utilities for handling note-related information, either incoming or outgoing are:

.cpsmidi // convert frequency to MIDI

.midicps // convert MIDI to frequency




// ====== THIRD PARTY IMPLEMENTATIONS ======

// There are a few libraries out there implementing MIDI functionality more extensively than what is there in the original distribution. These are all Quarks that you can download. 

// The dewdrop library is the quark with the most midi implementations, and it's well worth checking out. wslib conains some useful utilities and nice GUI, and JITMIDIKtl has some classes that are created to receive data from specific devices.



/* 

// ------ from dewdrop lib (quark) --

Voicer // (voice stealing implementation)

BasicMIDISocket

VoicerMIDISocket

LinkedVoicerSequencer

BasicMIDIControl

CCAllocator

CControl

MIDIBufManager

MBM (MIDIBufManager)

MIDIRecBuf

timeline-overview

miditest

*/



/*

// ------ from wslib (quark) --

== 

SimpleMIDIFile //(MIDI file read-write)

// GUIs

MIDIWindow  // a graphic interface for connecting to different ports

MIDIBendWindow // a graphic interface for midi pitchbend data.

MIDIKeyboard // a keyboard GUI widget

// utilities

midiname //notenumber to notename and backwards conversion

*/



/*

// ------ JITMIDIKtl (quark) --

// useful for hooking up specific devices

MIDIKtl

MIDIKtl_howTo

// currently (June 2010) supports:

PFKtl, PDKtl, NanoKtl, BCRKtl, FFLV2Ktl,  UC33Ktl

*/



/*

// ------ rd_ctl (quark) --

Controller // create a control GUI interface, easily mapable to MIDI, and with some curve mapping functions

*/


//-----------------------------------------------

// reading: Chapter 12: 'Real-time performance of computer music', from 'Computer Music', by Charles Dodge is a good reference for using MIDI devices in electronic music performance